﻿using System;
using System.Collections.Generic;
using System.Threading;
using CriWare;
using CriWare.CriTimeline.Mana;
using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.Logic;
using LFloatMath.Math;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

namespace IQIGame.Onigao.GamePlay
{
    public class BattleTimelineComponent : TComponent
    {
        public UnitView unit;

#region Timeline 播放期间参数

        public TimelineInfo curTimeline { get; private set; }
        public ETimelineState curState { get; private set; }
        private double time;
        private bool unscaleTime;
        private bool startWithCurrent;
        private CancellationTokenSource cancellation;
        private CancellationTokenSource delayCancellation; // timeline播放时的保底回调
        private bool playBack;

#endregion

#region 组件

        private PlayableDirector playableDirector => this.viewComp.playableDirector;
        private ExSignalReceiverBinding signalReceiver => this.viewComp.signalReceiver;
        private EntityDriver driver => this.viewComp.driver;

#endregion

        public bool enableLoopClip;

        private Dictionary<string, TimelineInfo> mTimelineCache = new Dictionary<string, TimelineInfo>();

        private Action onPlayStoped;

        public long curTimelinePart { get; private set; }
        public int curEffectiveIndex; // 当前播放的效果Index
        public int curTimelineSignaleTrigger { get; private set; } // 当前已经触发的技能效果信号点个数
        private double closeUpEnd = 0; // 特写结束信号点时间，若没有就是0

        // 缓存
        private UnitViewComponent viewComp;
        private ResLoader loader => this.viewComp.resLoader;
        private Action<UnitView, ExSignalEmitterData> onTimelineSignalEvent;


        public BattleCommand_StartPart partCommand { get; private set; }

        // 当前part带来的所有目标列表
        public List<int>[] curPartTargetList;
        // 当前part带来的目标点坐标
        public LVector2 curPartTargetPos;

        public override void OnCreate(TEntity entity)
        {
            this.unit = entity as UnitView;
        }

        public void Initialize()
        {
            this.viewComp = this.unit.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent);
            this.curState = ETimelineState.WaitForPlay;
        }

        protected override void OnDispose()
        {
            this.Stop();
        }

#region Timeline播放控制

        public async UniTask PlayTimeline(string rKey, BattleCommand_StartPart rCommand, Action<UnitView, ExSignalEmitterData> onEffectiveCallback = null)
        {
            // 记录当前要播放的Part, 编辑器模式下无所谓
            this.curTimelinePart = rCommand?.partGUID ?? 0;
            if (onEffectiveCallback == null)
            {
                onEffectiveCallback = this.TriggerTimelineSignal;
            }
            await this.Play(this.GetTimelineLoadPath(rKey), rCommand, onEffectiveCallback);
        }

        private async UniTask Play(string path, BattleCommand_StartPart rCommand, Action<UnitView, ExSignalEmitterData> onEffectiveCallback)
        {
            this.curState = ETimelineState.Playing;
            this.curTimelineSignaleTrigger = -1;
            
            var rInfo = await this.GetTimelineAsset(path);
            if (rInfo == null || rInfo.asset == null)
            {
                this.curState = ETimelineState.WaitForPlay;
                return;
            }
            
            this.cancellation?.Cancel();
            this.cancellation = new CancellationTokenSource();
            
            if (!rInfo.firstFrameWithCameraTrack && !BattleClientUtil.IsEditorMode)
            {
                BattleManager.Instance.ctrl.sceneCtrl.ResetCamera();
            }
            this.UnRegisterTimelineEvent(onEffectiveCallback);

            this.curTimeline = rInfo;
            this.RegisterTimelineEvent(onEffectiveCallback);
            this.playableDirector.playableAsset = rInfo.asset.asset;
            var rAnimationComp = this.unit.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent);
            rAnimationComp.Disable();
            this.partCommand = rCommand;

            var duration = this.playableDirector.duration;
            var fStartTime = this.playBack ? duration : 0;

            this.playableDirector.initialTime = fStartTime;
            if (!this.startWithCurrent)
            {
                this.time = fStartTime;
            }
            try
            {
                if (!this.playBack)
                {
                    var rTask = this.PlayTimeline(this.cancellation);
                    await rTask;
                }
                else
                {
                    await this.PlayTimelineFrame(duration, this.cancellation);
                }
            }
            finally
            {
                this.onPlayStoped?.Invoke();
                this.OnStop(onEffectiveCallback);
                this.partCommand = null;
                this.curTimeline = null;
                this.cancellation.Dispose();
                this.cancellation = null;
            }
        }

        private UniTask PlayTimeline(CancellationTokenSource cancel)
        {
            AutoResetUniTaskCompletionSource tcs = AutoResetUniTaskCompletionSource.Create();
            this.playableDirector.time = 0;
            this.playableDirector.stopped += (pd) =>
            {
                if (tcs != null)
                {
                    // 注意！这里要先置空，再调用，TrySetResult会先调用await之后的代码，如果await之后有给TaskSource赋新值，然后这里再置空，那么_completeActTaskSource就永远为null了。
                    // 若置空不及时导致了多次source.TrySetResult，会导致池内数据异常
                    var rTcs = tcs;
                    tcs = null;
                    rTcs.TrySetResult();
                }
                if (this.delayCancellation != null)
                {
                    var rDelayCancellation = this.delayCancellation;
                    this.delayCancellation = null;
                    rDelayCancellation.Cancel();
                }
            };

            this.playableDirector.Play();
            if (cancel != default)
            {
                cancel.Token.Register(
                    () =>
                    {
                        if (tcs != null)
                        {
                            // 注意！这里要先置空，再调用，TrySetResult会先调用await之后的代码，如果await之后有给TaskSource赋新值，然后这里再置空，那么_completeActTaskSource就永远为null了。
                            // 若置空不及时导致了多次source.TrySetResult，会导致池内数据异常
                            var rTcs = tcs;
                            tcs = null;
                            rTcs.TrySetCanceled();
                        }
                        if (this.delayCancellation != null)
                        {
                            var rDelayCancellation = this.delayCancellation;
                            this.delayCancellation = null;
                            rDelayCancellation.Cancel();
                        }
                    });
            }
            // 超时主动停止 多延迟0.1秒
            var nDuration = (int)(this.playableDirector.duration * 1000) + 100;
            this.StartTimelineDelayCallback(nDuration);

            return tcs.Task;
        }

        private async UniTask PlayTimelineFrame(double duration, CancellationTokenSource cancel)
        {
            this.playableDirector.time = this.time;
            this.playableDirector.Evaluate();

            var nBack = this.playBack ? -1 : 1;
            await UniTask.Yield(cancel.Token);

            while (this.time <= duration && this.time >= 0)
            {
                if (this.curState != ETimelineState.Pause)
                {
                    var deltaTime = this.unscaleTime ? Time.unscaledDeltaTime : Time.deltaTime;
                    this.time += deltaTime * nBack;
                    if (this.time > duration)
                    {
                        this.time = duration;
                        playableDirector.time = this.time;
                        playableDirector.Evaluate();
                        break;
                    }
                    else if (this.time < 0)
                    {
                        this.time = 0;
                        playableDirector.time = this.time;
                        playableDirector.Evaluate();
                        break;
                    }
                    playableDirector.time = this.time;
                    playableDirector.Evaluate();
                }
                await UniTask.Yield(cancel.Token);
            }
        }

        /// <summary>
        /// 运行中使用传统播放方法，倒播才会按帧播，此方法仅作保留参考
        /// </summary>
        /// <param name = "path"></param>
        /// <param name = "rCommand"></param>
        /// <param name = "onEffectiveCallback"></param>
        [Obsolete]
        private async UniTask PlayFrame(string path, BattleCommand_StartPart rCommand, Action<UnitView, ExSignalEmitterData> onEffectiveCallback)
        {
            this.curState = ETimelineState.Playing;
            this.curTimelineSignaleTrigger = -1;
            var rInfo = await this.GetTimelineAsset(path);
            if (rInfo == null || rInfo.asset == null)
            {
                this.curState = ETimelineState.WaitForPlay;
                return;
            }
            if (!rInfo.firstFrameWithCameraTrack)
            {
                BattleManager.Instance.ctrl.sceneCtrl.ResetCamera();
            }
            this.UnRegisterTimelineEvent(onEffectiveCallback);
            this.partCommand = rCommand;
            this.cancellation?.Cancel();
            this.cancellation = new CancellationTokenSource();
            this.curTimeline = rInfo;

            this.RegisterTimelineEvent(onEffectiveCallback);
            this.playableDirector.playableAsset = rInfo.asset.asset;
            var rAnimationComp = this.unit.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent);
            rAnimationComp.Disable();

            var nBack = this.playBack ? -1 : 1;
            playableDirector.initialTime = nBack;
            var duration = playableDirector.duration;
            var fStartTime = this.playBack ? duration : 0;
            if (!this.startWithCurrent)
            {
                this.time = fStartTime;
            }
            playableDirector.time = this.time;
            playableDirector.Evaluate();
            try
            {
                await UniTask.Yield(this.cancellation.Token);

                while (this.time <= duration && this.time >= 0)
                {
                    if (this.curState != ETimelineState.Pause)
                    {
                        var deltaTime = this.unscaleTime ? Time.unscaledDeltaTime : Time.deltaTime;
                        this.time += deltaTime * nBack;
                        if (this.time > duration)
                        {
                            this.time = duration;
                            playableDirector.time = this.time;
                            playableDirector.Evaluate();
                            break;
                        }
                        else if (this.time < 0)
                        {
                            this.time = 0;
                            playableDirector.time = this.time;
                            playableDirector.Evaluate();
                            break;
                        }
                        playableDirector.time = this.time;
                        playableDirector.Evaluate();
                    }
                    await UniTask.Yield(this.cancellation.Token);
                }
            }
            finally
            {
                this.OnStop(onEffectiveCallback);
            }

            this.onPlayStoped?.Invoke();

            this.partCommand = null;
            this.curTimeline = null;
            this.curState = ETimelineState.WaitForPlay;
        }

        /// <summary>
        /// timeline播放的时间保底，用来保证stop一定会被调用,
        /// 如果enableLoopClip = true,就强行增加20秒延时, 临时这么定，有需求再改
        /// </summary>
        /// <param name = "nTime"></param>
        private void StartTimelineDelayCallback(int nTime)
        {
            if (this.enableLoopClip)
            {
                nTime += 20000;
            }
            this.delayCancellation?.Cancel();
            this.delayCancellation = new CancellationTokenSource();
            BattleClientUtil.DelayInvoke(nTime,
                () =>
                {
                    if (this.playableDirector)
                    {
                        this.playableDirector.Stop();
                    }
                }, this.delayCancellation.Token).Forget();
        }

        public void Stop()
        {
            this.cancellation?.Cancel();
            this.OnStop(null);
        }

        public void Pause()
        {
            if (this.curState == ETimelineState.Playing)
            {
                this.curState = ETimelineState.Pause;
                this.delayCancellation?.Cancel();
                this.playableDirector.Pause();
            }
        }

        public void Resume()
        {
            if (this.curState == ETimelineState.Pause)
            {
                this.playableDirector.Play();
                this.curState = ETimelineState.Playing;
                var nDuration = (int)(this.playableDirector.duration - this.playableDirector.time) * 1000 + 100;
                this.StartTimelineDelayCallback(nDuration);
            }
        }

        public void JumpCloseUpEnd()
        {
            if (this.closeUpEnd != 0)
            {
                this.JumpTimeline(time);
            }
            else if (this.curState == ETimelineState.Pause)
            {
                this.Resume();
            }
        }

        private Dictionary<string, int> _timelineSignalCallback = new Dictionary<string, int>();

        public void AddWaitingQueue(string key, int responseGUID)
        {
            BattleManager.Instance.ctrl.queueCtrl.AddWaitingQueue(responseGUID);
            this._timelineSignalCallback[key] = responseGUID;
        }

        private void TriggerSignalCallback(string key)
        {
            if (this._timelineSignalCallback.TryGetValue(key, out var nRespGUID))
            {
                BattleManager.Instance.ctrl.queueCtrl.OnWaitComplete(nRespGUID).Forget();
            }
        }

        // 信号点触发
        private void TriggerTimelineSignal(UnitView unit, ExSignalEmitterData signalData)
        {
            switch (signalData.type)
            {
                case EExSignalType.SkillEffect:
                {
                    // 技能效果的触发由队列控制等待，这里不需要特殊处理
                    this.curTimelineSignaleTrigger++;
                    break;
                }
                case EExSignalType.CloseUp:
                {
                    var rVideo = signalData.paramStr[0];
                    if (!string.IsNullOrEmpty(rVideo))
                    {
                        var param = new CloseUpParam
                        {
                            video = rVideo,
                            unit = unit
                        };
                        BattleManager.Instance.ctrl.closeUpCtrl.Active(param).Forget();
                    }
                    break;
                }
                case EExSignalType.Callback:
                {
                    var rTimelieComp = unit.GetComponent<BattleTimelineComponent>(ETComponentType.BattleTimelineComponent);
                    rTimelieComp.TriggerSignalCallback(signalData.paramStr.Get(0));
                    break;
                }
                case EExSignalType.FakeHit:
                    var rEffectComp = unit.GetComponent<EffectComponent>(ETComponentType.EffectComponent);
                    var nDamageType = signalData.paramInt.Get(0);
                    var nEffectiveIndex = signalData.paramInt.Get(1);
                    string rHitEffectPath = rEffectComp.GetHitEffectBySkill((EDamageType)(1 << nDamageType), this.partCommand?.skillID ?? 0);
                    var rTargets = this.GetCurTarget(nEffectiveIndex);
                    for (int i = 0; i < rTargets.Count; i++)
                    {
                        var rTarget = BattleManager.Instance.ctrl.data.GetUnit(rTargets[i]);
                        if (rTarget != null)
                        {
                            var rTargetEffect = rTarget.GetComponent<EffectComponent>(ETComponentType.EffectComponent);
                            rTargetEffect.AddHitEffect(rHitEffectPath);
                            var rViewComp = rTarget.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent);
                            rViewComp.OnHit();
                        }
                    }

                    break;
            }
        }

#endregion

        private void OnStop(Action<UnitView, ExSignalEmitterData> onEffectiveCallback)
        {
            this.playableDirector.Stop();

            var rAnimationComp = this.unit.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent);
            rAnimationComp.Enable();

            this.UnRegisterTimelineEvent(onEffectiveCallback);
#if UNITY_EDITOR
            if (!BattleClientUtil.IsEditorMode)
#endif
            {
                this.curState = ETimelineState.WaitForPlay;
            }
        }

        private void JumpTimeline(double time)
        {
            if (this.curState == ETimelineState.Playing || this.curState == ETimelineState.Pause)
            {
                this.time = time;
                this.Resume();
            }
        }

        /// <summary>
        /// 获取timeline资源，包含timeline资源的初始化预处理
        /// </summary>
        /// <param name = "path"></param>
        /// <returns></returns>
        private async UniTask<TimelineInfo> GetTimelineAsset(string path)
        {
            if (this.mTimelineCache.TryGetValue(path, out var rInfo))
            {
                this.closeUpEnd = rInfo.closeUpEnd;
                return rInfo;
            }

            TimelineAssetInfo rAsset = null;
#if UNITY_EDITOR
            if (BattleClientUtil.IsEditorMode)
            {
                rAsset = new TimelineAssetInfo()
                {
                    asset = UnityEditor.AssetDatabase.LoadAssetAtPath<TimelineAsset>("Assets/" + path),
                };
            }
            else
#endif
            {
                rAsset = await ManagerCenter.Timeline.Get(path);
            }

            if (rAsset == null || rAsset.asset == null)
            {
                LogGame.LogError("未获取到timeline资源" + path);
                return null;
            }
            rInfo = new TimelineInfo()
            {
                asset = rAsset
            };
            
            this.playableDirector.playableAsset = rInfo.asset.asset;
            // 绑定
            var rBindingOutputs = new List<PlayableBinding>(rAsset.asset.outputs);
            for (int i = 0; i < rBindingOutputs.Count; i++)
            {
                var rTrackObj = rBindingOutputs[i].sourceObject;
                if (rTrackObj == null) continue;
                switch (rTrackObj)
                {
                    case AnimationTrack rAnimeTrack:
                    {
                        if (this.playableDirector.GetGenericBinding(rAnimeTrack) == null)
                        {
                            var rAnimationComp = this.unit.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent);
                            rAnimationComp.Disable();
                            this.playableDirector.SetGenericBinding(rAnimeTrack, rAnimationComp.animator);
                        }
                        break;
                    }
                    case ExSignalBindingTrack rBindingSignalTrack:
                    {
                        if (this.playableDirector.GetGenericBinding(rBindingSignalTrack) == null)
                        {
                            this.playableDirector.SetGenericBinding(rBindingSignalTrack, this.signalReceiver);
                        }
                        var rMakers = rBindingSignalTrack.GetMarkers();
                        foreach (var rMaker in rMakers)
                        {
                            if (!(rMaker is ExtendExSignalEmitter rSignal))
                            {
                                continue;
                            }
                            for (int j = 0; j < rSignal.signals.Count; j++)
                            {
                                var rData = rSignal.signals[j];
                                switch (rData.type)
                                {
                                    case EExSignalType.CloseUp:
                                    {
                                        if (rData.paramStr == null || rData.paramStr.Length <= 0 || string.IsNullOrEmpty(rData.paramStr[0]))
                                        {
                                            rInfo.closeUpEnd = rMaker.time;
                                            break;
                                        }
                                        if (rInfo.closeUpEnd == 0)
                                        {
                                            rInfo.closeUpEnd = rMaker.time;
                                        }
                                        break;
                                    }
                                    case EExSignalType.SkillEffect:
                                    {
                                        rInfo.effectSignals.Add(rData);
                                        break;
                                    }
                                }
                            }
                        }
                        break;
                    }
                    case UnitEffectTrack:
                    {
                        if (this.playableDirector.GetGenericBinding(rTrackObj) == null)
                        {
                            this.playableDirector.SetGenericBinding(rTrackObj, this.driver);
                        }
                        var rRefValue = this.playableDirector.GetReferenceValue(TimelineAssetInfo.RefInfoID, out var bIsValid);
                        if (!bIsValid || !(rRefValue is TimelineAssetInfoHandler rHandler))
                        {
                            rHandler = ScriptableObject.CreateInstance<TimelineAssetInfoHandler>();
                            this.playableDirector.SetReferenceValue(TimelineAssetInfo.RefInfoID, rHandler);
                        }
                        rHandler.assetInfo = rAsset;
                        break;
                    }
                    case DummyHideTrack:
                    {
                        if (this.playableDirector.GetGenericBinding(rTrackObj) == null)
                        {
                            this.playableDirector.SetGenericBinding(rTrackObj, this.driver);
                        }
                        break;
                    }
                    case CriManaTrack:
                    {
                        if (this.playableDirector.GetGenericBinding(rTrackObj) == null)
                        {
                            CriManaMovieMaterial rRawVideoPlayer = null;
#if UNITY_EDITOR
                            if (BattleClientUtil.IsEditorMode)
                            {
                                rRawVideoPlayer = SkillEditorMainUpdate.Instance.videoPlayer;
                            }
                            else
#endif
                            {
                                rRawVideoPlayer = CommonVideoPlayManager.Instance.GetOrCreatePlayer().rawVideoPlayer;
                            }
                            this.playableDirector.SetGenericBinding(rTrackObj, rRawVideoPlayer);
                        }
                        break;
                    }
                    case UnitCameraTrack rCameraTrack:
                    {
                        if (this.playableDirector.GetGenericBinding(rTrackObj) == null)
                        {
                            this.playableDirector.SetGenericBinding(rTrackObj, this.driver);
                        }
                        rInfo.firstFrameWithCameraTrack = rCameraTrack.start == 0;
                        break;
                    }
                }
            }
            this.closeUpEnd = rInfo.closeUpEnd;
            this.mTimelineCache.TryAdd(path, rInfo);
            return rInfo;
        }

        private string GetTimelineLoadPath(string timelineName)
        {
            var rResName = this.viewComp.curRes.resName;
            if (this.viewComp.curRes.entityType == 0)
            {
                return PathConstant.GetHeroTimelinePath(rResName, timelineName);
            }
            else
            {
                return PathConstant.GetMonsterTimelinePath(rResName, timelineName);
            }
        }

        /// <summary>
        /// 获取当前播放的timeline所对应技能指向的目标
        /// </summary>
        /// <param name = "effectiveIndex">-1 全部 0 默认（下一段技能效果） >0 对应技能效果的目标</param>
        /// <returns></returns>
        public List<int> GetCurTarget(int effectiveIndex)
        {
            List<int> rTargetList = null;
            if (effectiveIndex >= 0)
            {
                effectiveIndex -= 1; // 配置的index从1开始，下标索引从0开始
                var nEffectiveIndex = this.curTimelineSignaleTrigger + 1; // 下一段技能效果
                if (effectiveIndex >= 0)
                {
                    nEffectiveIndex = effectiveIndex;
                }
                if (nEffectiveIndex >= this.curPartTargetList.Length)
                {
                    rTargetList = this.curPartTargetList?.Get(this.curTimelineSignaleTrigger);
                }
                else
                {
                    rTargetList = this.curPartTargetList?.Get(nEffectiveIndex);
                }
            }
            else
            {
                rTargetList = new List<int>();
                for (int i = 0; i < this.curPartTargetList.Length; i++)
                {
                    rTargetList.AddRange(this.curPartTargetList[i]);
                }
            }
            return rTargetList;
        }

        public Vector3 GetCurTargetPos(int effectiveIndex)
        {
            var rTarget = this.GetCurTarget(effectiveIndex);
            if (rTarget != null)
            {
                if (rTarget.Count > 0)
                {
                    var rNextTarget = BattleManager.Instance.ctrl.data.GetUnit(rTarget[0]);
                    if (rNextTarget != null)
                    {
                        return rNextTarget.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent).position;
                    }
                }
            }
            return this.unit.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent).position;
        }

        private void RegisterTimelineEvent(Action<UnitView, ExSignalEmitterData> onEvent)
        {
            signalReceiver.RegisterAction(string.Empty, OnEffectCallBack);
            this.onTimelineSignalEvent += onEvent;
        }

        private void UnRegisterTimelineEvent(Action<UnitView, ExSignalEmitterData> onEvent)
        {
            signalReceiver.UnRegisterAction(string.Empty, OnEffectCallBack);
            if (onEvent == null)
            {
                this.onTimelineSignalEvent = null;
            }
            else
            {
                this.onTimelineSignalEvent -= onEvent;
            }
        }

        private void OnEffectCallBack(ExSignalReceiverBinding binding, ExSignalEmitter signal)
        {
            if (binding != this.signalReceiver)
            {
                return;
            }
            if (signal is ExtendExSignalEmitter e)
            {
                for (int i = 0; i < e.signals.Count; i++)
                {
                    this.onTimelineSignalEvent?.Invoke(this.unit, e.signals[i]);
                }
            }
        }

        public enum ETimelineState
        {
            Node,
            WaitForPlay,
            Playing,
            Pause,
        }

        public class TimelineInfo
        {
            public TimelineAssetInfo asset;
            public double closeUpEnd;
            public List<ExSignalEmitterData> effectSignals = new List<ExSignalEmitterData>();
            public bool firstFrameWithCameraTrack;
        }
    }
}